﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace ApplicationLayer
{
    public class Game
    {
        public GameBoard board = new GameBoard();
        public List<Player> players = new List<Player>();
        public int numPlayers;
        public int currentTurn;
        public bool isDaemonOn;
        public int daemonTimer;
        public int playerTurnTimeLimit;
        public int gameTimeLimit;
        public int[,] boardMap;
        public static int EMPTY = -1;
        public static int UNPLAYABLE = -2;
        public static int STARTING = -3;

        public void setup(GameDesign gameDesign, List<Player> pls)
        {
            if (gameDesign == null)
                return;
            //deep copy of board
            board.numColumns = gameDesign.board.numColumns;
            board.numRows = gameDesign.board.numRows;
            foreach (Point p in gameDesign.board.unplayable)
            {
                Point tempPoint = new Point(p.X, p.Y);
                board.unplayable.Add(tempPoint);
            }
            foreach (Point p in gameDesign.board.startingCells)
            {
                Point tempPoint = new Point(p.X, p.Y);
                board.startingCells.Add(tempPoint);
            }

            //create players
            numPlayers = gameDesign.maxPlayers;
            int ct = 0;
            foreach (Player pl in pls) {
                Player player = pl.getCopy();
                if (ct == 0)
                    player.color = Color.Blue;
                else if (ct == 1)
                    player.color = Color.Red;
                else if (ct == 2)
                    player.color = Color.Yellow;
                else if (ct == 3)
                    player.color = Color.Green;
                //deep copy of pieces
                foreach (GamePiece gp in gameDesign.pieces)
                {
                    MyPiece piece = new MyPiece();
                    piece.isAvailable = true;
                    foreach (Point p in gp.points)
                    {
                        Point tempPoint = new Point(p.X, p.Y);
                        piece.points.Add(tempPoint);
                    }
                    player.score -= gp.points.Count;
                    player.pieces.Add(piece);
                }

                //set boardmap
                player.playerMap = new int[board.numRows, board.numColumns];
                for (int rw = 0; rw < board.numRows; rw++)
                {
                    for (int col = 0; col < board.numRows; col++)
                    {
                        player.playerMap[rw, col] = Player.NEUTRAL;
                    }
                }
                players.Add(player);
                ct++;
            }

            numPlayers = 4;
            isDaemonOn = gameDesign.isDaemonOn;
            daemonTimer = gameDesign.daemonTimer;
            playerTurnTimeLimit = gameDesign.playerTurnTimeLimit;
            gameTimeLimit = gameDesign.gameTimeLimit;



            //create boardMap
            boardMap = new int[board.numRows, board.numColumns];
            for (int rw = 0; rw < board.numRows; rw++)
            {
                for (int col = 0; col < board.numColumns; col++)
                {
                    boardMap[rw, col] = EMPTY;
                }
            }
            foreach (Point p in board.unplayable)
                boardMap[p.Y, p.X] = UNPLAYABLE;
            foreach (Point p in board.startingCells)
                boardMap[p.Y, p.X] = STARTING;
        }

        //attempts to place the piece at the specified location on the board
        public bool PlacePiece(int newX, int newY, int pieceSelected)
        {
            bool isValidPlace = true;
            bool hasValidStart = false;
            foreach (Point p in this.players[this.currentTurn].pieces[pieceSelected].points)
            {
                if (newX + p.X < 0 || newX + p.X >= this.board.numColumns)
                {
                    isValidPlace = false;
                    break;
                }
                if (newY + p.Y < 0 || newY + p.Y >= this.board.numRows)
                {
                    isValidPlace = false;
                    break;
                }
                if (this.boardMap[newY + p.Y, newX + p.X] == ApplicationLayer.Game.UNPLAYABLE || this.boardMap[newY + p.Y, newX + p.X] >= 0
                    || this.players[this.currentTurn].playerMap[newY + p.Y, newX + p.X] == Player.SIDE)
                {
                    isValidPlace = false;
                    break;
                }
                //check for valid corner placing location (or starting cell if no pieces have been played yet)
                if (this.boardMap[newY + p.Y, newX + p.X] == ApplicationLayer.Game.STARTING || this.players[this.currentTurn].playerMap[newY + p.Y, newX + p.X] == Player.CORNER)
                {
                    hasValidStart = true;
                }
            }
            //valid placement, so place piece
            if (isValidPlace && hasValidStart)
            {
                int[] xs = { 0, -1, 1, 0 };
                int[] ys = { -1, 0, 0, 1 };
                int[] xc = { -1, 1, -1, 1 };
                int[] yc = { -1, -1, 1, 1 };
                foreach (Point p in this.players[this.currentTurn].pieces[pieceSelected].points)
                {
                    this.boardMap[newY + p.Y, newX + p.X] = this.currentTurn;
                    for (int ct = 0; ct < 4; ct++)
                    {
                        if (newX + p.X + xs[ct] < 0 || newX + p.X + xs[ct] >= this.board.numColumns ||
                            newY + p.Y + ys[ct] < 0 || newY + p.Y + ys[ct] >= this.board.numRows)
                            continue;
                        this.players[this.currentTurn].playerMap[newY + p.Y + ys[ct], newX + p.X + xs[ct]] = Player.SIDE;
                    }
                    for (int ct = 0; ct < 4; ct++)
                    {
                        if (newX + p.X + xc[ct] < 0 || newX + p.X + xc[ct] >= this.board.numColumns ||
                            newY + p.Y + yc[ct] < 0 || newY + p.Y + yc[ct] >= this.board.numRows)
                            continue;
                        if (this.players[this.currentTurn].playerMap[newY + p.Y + yc[ct], newX + p.X + xc[ct]] != Player.SIDE)
                            this.players[this.currentTurn].playerMap[newY + p.Y + yc[ct], newX + p.X + xc[ct]] = Player.CORNER;
                    }
                }
                //next turn
                this.players[this.currentTurn].pieces[pieceSelected].isAvailable = false;
                nextTurn();
                return true; //returns true if piece is successfully placed
            }
            return false; //returns false if piece fails to place
        }

        public void nextTurn()
        {
            this.currentTurn++;
            if (this.currentTurn >= this.numPlayers)
                this.currentTurn = 0;
            while (this.players[this.currentTurn].gameover)
            {
                this.currentTurn++;
                if (this.currentTurn >= this.numPlayers)
                    this.currentTurn = 0;
            }
        }

        public void daemonAttacks()
        {
            //gather all possible choices for the attack
            List<Point> possibleAttacks = new List<Point>();
            for (int rw = 0; rw < board.numRows; rw++)
            {
                for (int col = 0; col < board.numColumns; col++)
                {
                    if (boardMap[rw, col] >= 0)
                    {
                        possibleAttacks.Add(new Point(col, rw));
                    }
                }
            }
            //daemon attacks
            if (possibleAttacks.Count > 0)
            {
                Random random = new Random();
                int attacked = random.Next(possibleAttacks.Count);
                boardMap[possibleAttacks[attacked].Y, possibleAttacks[attacked].X] = -1;
            }
        }
    }
}
